gusucode.com > VC++ EMF图片浏览器(可读emf、wmf、emz、wmz、png……等)-源码程序 > VC++ EMF图片浏览器(可读emf、wmf、emz、wmz、png……等)-源码程序/code/Src/Client/scemflib/SCEMFConverter.cpp

    //Download by http://www.NewXing.com
/*
*	This file is part of the EMFexplorer projet.
*	Copyright (C) 2004 Smith Charles.
*
*	This library is free software; you can redistribute it and/or
*	modify it under the terms of the GNU Lesser General Public
*	License as published by the Free Software Foundation; either
*	version 2.1 of the License, or (at your option) any later version.
*
*   This library is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   Lesser General Public License for more details.
*
*   You should have received a copy of the GNU Lesser General Public
*   License along with this library; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*
*	Extension: for commercial use, apply the Equity Public License, which
*	adds to the normal terms of the GLPL a condition of donation to the author.
*   If you are interested in support for this source code,
*   contact Smith Charles <smith.charles@free.fr> for more information.
*/


#include "stdafx.h"
#include "SCEMFConverter.h"

#include "SCEMF.h"
#include "SCEMFRasterizer.h"

#include "SCGenInclude.h"
#include SC_INC_WINLIB(SCWinFile.h)
#include SC_INC_WINLIB(SCBitmap.h)
#include <afxext.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

SCEMFConverter::SCEMFConverter():
	m_pEMFDoc(NULL),
	m_iCurZoom(SC_ZOOM100),
	m_rectMargins(0,0,0,0),
	m_bReverseVideo(FALSE),
	m_uiRasEngine(SC_ENGINE_GDIP),
	m_crPaperColor(RGB(255,255,255)),
	m_rectClient(0,0,0,0),
	m_iFitMode(SC_FIT_NONE),
	//
	m_iCtxAngle(0),
	m_iCtxPage(0),
	m_rcCtxDest(0,0,0,0),
	m_bCtxMeta(FALSE),
	m_hdcCtxAttrib(NULL),
	m_bCtxSaveAsIs(TRUE),
	m_bCtxInvert(FALSE)
{
}

SCEMFConverter::~SCEMFConverter()
{
}

// Save/Copy/Print
void SCEMFConverter::SCDisplayPageEx(HDC hDC, HENHMETAFILE hEmf,
									   CRect& rcDest, CRect& rcPaper,
									   SCGDIpDrawingAttributes& rAttributes,
									   int iAngle/*=0*/, int iZoom/*=SC_ZOOM100*/,
									   BOOL bInvert/*=FALSE*/)
{
	// Paper
	CRect PaperRect = rcPaper;

	// Position of the EMF rectangle in world coordinates for playing
	CSize sizeEMF;
	SCGetEMFPlaySize(hEmf, sizeEMF);

	int iPlayX = rcDest.left - PaperRect.left;
	int iPlayY = rcDest.top - PaperRect.top;
	CRect rcPlay(iPlayX, iPlayY,
		iPlayX + MulDiv(sizeEMF.cx, iZoom, SC_ZOOM100),
		iPlayY + MulDiv(sizeEMF.cy, iZoom, SC_ZOOM100));

	int iState = SaveDC(hDC);
	::IntersectClipRect(hDC, rcDest.left, rcDest.top, rcDest.right, rcDest.bottom);

	int iGrOldMode;
	if (iAngle)
	{// Position of the PAGE rectangle for rotation in world coordinates
		CRect rcRotate = PaperRect;
		rcRotate.OffsetRect(iPlayX, iPlayY);
		SCRotateDC(hDC, iAngle, rcRotate, rcDest.left, rcDest.top, iGrOldMode);
	}
	
	// Rasterize
	BOOL bOK = FALSE;
	if (SCGdipGetEMFType(hEmf)!=EmfTypeEmfOnly)
		bOK = SCGdipPlayMetafile(hDC, hEmf, rcPlay);
	else
	if (SC_ENGINE_GDIP==m_uiRasEngine)
	{
		SCEMFRasterizer Rasterizer;
		CSCEMFdcRenderer& rRenderer = Rasterizer.SCGetRenderer();
		rRenderer.SCSetDrawingAttributes(rAttributes);
		Rasterizer.SCBreakMetafile(hDC, hEmf, NULL, (LPRECT)&rcPlay);
		bOK = TRUE;
	} else
	{
		bOK = PlayEnhMetaFile(hDC, hEmf, (LPRECT)&rcPlay);
	}
	if (m_bCtxSaveAsIs)
		SCPostRender(m_hdcCtxAttrib, hDC, NULL);

	// Restore DC state
	RestoreDC(hDC, iState);

	// Perform full rvideo (AFTER restore DC to prevent gaps due to rounding errors)
	if (bInvert)
		PatBlt(hDC, rcDest.left, rcDest.top, rcDest.Width(), rcDest.Height(), DSTINVERT);
}

#define SC_USING_TRICK81

///
/// Copy the given page to another format
///
HANDLE SCEMFConverter::SCCopyPage(int iPage,
								  int iOutputType, int iSubType, BOOL bSaveAsIs)
{
	ASSERT(m_pEMFDoc);
	if (!m_pEMFDoc)
		return NULL;

	ASSERT(iPage>=1 && iPage<=(int)m_pEMFDoc->SCGetNbPages());
	HENHMETAFILE hEMF = m_pEMFDoc->SCGetPageEMF(iPage-1);
	if (!hEMF)
		return NULL;

	m_iCtxPage = iPage;
	m_bCtxSaveAsIs = bSaveAsIs;

	HANDLE hRes = NULL;
	CSize sizeEMF;
	SCGetEMFPlaySize(hEMF, sizeEMF);
	
	COLORREF crBkColor = RGB(255, 255, 255);
	SCGDIpDrawingAttributes rAttributes = m_CtxDrawingAttributes;

	CRect PaperRect;
	if (bSaveAsIs)
	{// sort of wysiwyg, including current zoom, borders, rotation, etc.
		PSCEMFDocPage pDocPage = m_pEMFDoc->SCGetDocPage(iPage-1);
		ASSERT(pDocPage);
		pDocPage->SCComputePageInfo(m_bReverseVideo, m_rectMargins);
		pDocPage->SCGetPageBlackBox(PaperRect);
		SCComputeFitZoom(PaperRect);

		PaperRect.InflateRect(m_rectMargins.left, m_rectMargins.top, m_rectMargins.right, m_rectMargins.bottom);
		PaperRect.left = MulDiv(PaperRect.left, m_iCurZoom, SC_ZOOM100);
		PaperRect.right = MulDiv(PaperRect.right, m_iCurZoom, SC_ZOOM100);
		PaperRect.top = MulDiv(PaperRect.top, m_iCurZoom, SC_ZOOM100);
		PaperRect.bottom = MulDiv(PaperRect.bottom, m_iCurZoom, SC_ZOOM100);
		
		// background
		crBkColor = (m_bReverseVideo && !SC_RVIDEOFULL(rAttributes))?
					SCGetReversedPaperColor() : SCGetTranslatedPaperColor();
		rAttributes.bBkSolid = (SCGetTranslatedPaperColor()!=RGB(255, 255, 255));
	} else
	{// convert only
		PaperRect.SetRect(0, 0, sizeEMF.cx, sizeEMF.cy);
		rAttributes.bBkSolid = FALSE;
	}

	CRect rcPlay(0, 0,	PaperRect.Width(), PaperRect.Height());
	m_rcCtxDest = rcPlay; // unrotated paper
	
	if (bSaveAsIs && (90==m_iCtxAngle || 270==m_iCtxAngle))
	{// rotate play rect, since the paper is not rotated
		SMC_ISWAP(rcPlay.left, rcPlay.top);
		SMC_ISWAP(rcPlay.right, rcPlay.bottom);
		SMC_ISWAP(sizeEMF.cx, sizeEMF.cy);
	}
	
	CSCMemDC MemDC;
	if (!MemDC.SCPrepareSurface(rcPlay.Width(), rcPlay.Height(), hEMF, crBkColor))
	{// A large zoom may require a lot of Megs.
		AfxMessageBox(SC_EMFLIB_UFMTERR_FSAVE);
		return NULL;
	}
	CDC* pDC = &MemDC;
	CMetaFileDC* pMeta = NULL;
	HBITMAP hMetaBm = NULL;
	HBITMAP hOldMetaBm = NULL;
	CRect rcMeta;
	m_hdcCtxAttrib = MemDC.m_hDC;
	m_bCtxInvert = (bSaveAsIs && SCIsFullReverseVideo());

	switch (iOutputType)
	{
	case SC_FTYPE_EMF:
	case SC_FTYPE_WMF:
		{
			m_bCtxMeta = TRUE;
			// create metafile
			rcMeta = rcPlay;
			SCRectLPtoHIMETRIC(pDC, &rcMeta);
#ifdef SC_USING_TRICK81
			{// Sorry for this. Any other technique is welcome.
				// Note: MemDC is based on screen, and GetDeviceCaps may return 96
				// MetaDC is based on MemDC should contain a picture at 96.
				// But the physical vs logical DPI mess in Windows yields 81.
				CMetaFileDC metaDC;
				metaDC.CreateEnhanced(&MemDC, NULL, &rcMeta, NULL);
				metaDC.SetAttribDC(MemDC.m_hDC);
				HENHMETAFILE hVoidEMF = metaDC.CloseEnhanced();
				if (hVoidEMF)
				{
					long lEmfDPIX = 0;
					long lEmfDPIY = 0;
					long lEmfPaperCx = 0;
					long lEmfPaperCy = 0;
					if (SCGetEMFInfos(hVoidEMF, lEmfDPIX, lEmfDPIY,
						lEmfPaperCx, lEmfPaperCy))
					{
						long lDevDPIX = MemDC.GetDeviceCaps(LOGPIXELSX);
						long lDevDPIY = MemDC.GetDeviceCaps(LOGPIXELSY);
						// Note: picture me be larger than expected
						rcMeta.right = MulDiv(rcMeta.right, lDevDPIX, lEmfDPIX);
						rcMeta.bottom = MulDiv(rcMeta.bottom, lDevDPIY, lEmfDPIY);
					}
					::DeleteEnhMetaFile(hVoidEMF);
				}
			}
#endif

			if (SC_ENGINE_GDIP==m_uiRasEngine ||
				EmfTypeEmfPlusOnly==SCGdipGetEMFType(hEMF))
			{
				EmfType eType = EmfTypeEmfOnly;
				switch (iSubType)
				{
				case SC_SUBTYPE_EMF_EMF: eType = EmfTypeEmfOnly; break;
				case SC_SUBTYPE_EMF_PLUS: eType = EmfTypeEmfPlusOnly; break;
				case SC_SUBTYPE_EMF_DUAL: eType = EmfTypeEmfPlusDual; break;
				default:
					ASSERT(0);
				}
				
				HENHMETAFILE hNewEMF;
				if (bSaveAsIs)
					hNewEMF = SCBBoxConvertEMFtoEMFp(hEMF, rAttributes, eType,
					rcPlay, rcMeta, PaperRect, MemDC.m_hDC,
					float(m_iCurZoom)/SC_ZOOM100, m_iCtxAngle, this);
				else
				{
					SCGDIpDrawingAttributes CleanAttributes;
					CleanAttributes.bBkSolid = FALSE;
					hNewEMF = SCConvertEMFtoEMFp(hEMF, CleanAttributes, eType);
				}
				
				ASSERT(hNewEMF);
				return (HANDLE)hNewEMF;
			}
			
			// Interpret GDI EMF or dual EMF, storing to GDI EMF
			hMetaBm = CreateCompatibleBitmap(MemDC.m_hDC, sizeEMF.cx, sizeEMF.cy);
			hOldMetaBm = (HBITMAP)SelectObject(MemDC.m_hDC, hMetaBm);
			
			pMeta = new CMetaFileDC;
			pMeta->CreateEnhanced(&MemDC, NULL, &rcMeta, NULL);
			pMeta->SetAttribDC(MemDC.m_hDC);
			pDC = pMeta;
			
			// background
			if (rAttributes.bBkSolid)
			{
				CBrush brush;
				brush.CreateSolidBrush(crBkColor);
				pDC->FillRect(&rcPlay, &brush);
			}
		}
		break;

		case SC_FTYPE_IMG:
			break;

		default:
			ASSERT(0);
	}

	m_bCtxInvert = FALSE; // prefer invert AFTER restoring DC 
	if (bSaveAsIs)
	{// sort of wysiwyg, including current zoom, borders, rotation, etc.
		SCDisplayPageEx(pDC->m_hDC, hEMF, rcPlay, PaperRect, rAttributes,
			m_iCtxAngle, m_iCurZoom, SCIsFullReverseVideo());
	} else
	{// convert only: save image at 100% with no rotation
		SCDisplayPageEx(pDC->m_hDC, hEMF, rcPlay, PaperRect, rAttributes);
	}
	
	switch (iOutputType)
	{
	case SC_FTYPE_EMF:
	case SC_FTYPE_WMF:
		{
			ASSERT(pMeta);
			hRes = (HANDLE)pMeta->CloseEnhanced();
			ASSERT(hRes);

			ASSERT(hMetaBm);
			SelectObject(MemDC.m_hDC, hOldMetaBm);
			DeleteObject(hMetaBm);
			delete pMeta;
		}
		break;
		
	case SC_FTYPE_IMG:
		{
			CBitmap *pBmp = MemDC.SCDetachDIB();
			ASSERT(pBmp);
			if (pBmp)
			{
				HBITMAP hBM = (HBITMAP)pBmp->Detach();
				delete pBmp;

				hRes = (HANDLE)SCCopyBitmap(hBM);
				DeleteObject(hBM);
			}
		}
	}

	return hRes;
}

void SCEMFConverter::SCPostRender(HDC hAttribDC, HDC hOutDC, GDPGraphics* pOutGraphics)
{
	PSCEMFDocPage pDocPage = m_pEMFDoc->SCGetDocPage(m_iCtxPage - 1);
	ASSERT(pDocPage);

	int xDest = m_rcCtxDest.left;
	int yDest = m_rcCtxDest.top;
	int iWdt = m_rcCtxDest.Width();
	int iHgt = m_rcCtxDest.Height();

	if (hOutDC)
	{
		if (m_bCtxMeta)
		{
			ASSERT(hAttribDC);
			pDocPage->SCDisplayPageComments(hOutDC, float(m_iCurZoom)/float(SC_ZOOM100),
			xDest, yDest, iWdt, iHgt, hAttribDC);
		} else
			pDocPage->SCDisplayPageComments(hOutDC, float(m_iCurZoom)/float(SC_ZOOM100),
			xDest, yDest, iWdt, iHgt);
		
		// Perform full rvideo
		if (m_bCtxInvert)
		{
			// this strange swap is because postrender is called before DC is unrotated
			switch (m_iCtxAngle)
			{
			case 90:
			case 270:
				SMC_ISWAP(iWdt, iHgt);
				break;
			}
			PatBlt(hOutDC, xDest-1, yDest-1, iWdt+2, iHgt+2, DSTINVERT);
		}
	} else
	{
		ASSERT(pOutGraphics);
		pDocPage->SCDisplayPageComments(pOutGraphics, float(m_iCurZoom)/float(SC_ZOOM100),
			xDest, yDest, iWdt, iHgt);

		// Perform full rvideo
		if (m_bCtxInvert)
		{
			// this strange swap is because postrender is called before DC is unrotated
			switch (m_iCtxAngle)
			{
			case 90:
			case 270:
				SMC_ISWAP(iWdt, iHgt);
				break;
			}
			hOutDC = pOutGraphics->GetHDC();
			ASSERT(hOutDC);

			PatBlt(hOutDC, xDest-1, yDest-1, iWdt+2, iHgt+2, DSTINVERT);

			pOutGraphics->ReleaseHDC(hOutDC);
		}
	}
}

void SCEMFConverter::SCComputeFitZoom(CRect& rcElems)
{
	if (SC_FIT_NONE==m_iFitMode)
		return;

	// account for extra margins
	CSize sizeEMF(rcElems.Width() + m_rectMargins.left + m_rectMargins.right + MIN_MARGIN*2,
		rcElems.Height() + m_rectMargins.top + m_rectMargins.bottom + MIN_MARGIN*2);

	// Rotate
	if ((90==m_iCtxAngle || 270==m_iCtxAngle))
	{
		SMC_ISWAP(sizeEMF.cx, sizeEMF.cy);
	}

	// Available client
	int iWndCx = m_rectClient.Width();
	int iWndCy = m_rectClient.Height();

	// scale
	float fScale;
	switch (m_iFitMode)
	{
	case SC_FIT_WIDTH:
		fScale = float(iWndCx)/float(sizeEMF.cx);
		break;

	case SC_FIT_PAGE:
		if (iWndCx>=iWndCy)
		{// attempt keep y, and reduce x
			fScale = float(iWndCy)/float(sizeEMF.cy);
			if (fScale*sizeEMF.cx > iWndCx)
				fScale = float(iWndCx)/float(sizeEMF.cx);
		} else
		{// attempt keep x, and reduce y
			fScale = float(iWndCx)/float(sizeEMF.cx);
			if (fScale*sizeEMF.cy > iWndCy)
				fScale = float(iWndCy)/float(sizeEMF.cy);
		}
		break;

	default:
		ASSERT(0);
		return;
	}

	m_iCurZoom = (int)(fScale*SC_ZOOM100);
	if (m_iCurZoom<SC_MIN_ZOOM)
		m_iCurZoom = SC_MIN_ZOOM;
	else
	if (m_iCurZoom>SC_MAX_ZOOM)
		m_iCurZoom = SC_MAX_ZOOM;
}